use std::{env, fs, io::ErrorKind};

struct GrepParams<'a> {
    query: &'a str,
    file_path: &'a str,
    ignore_case: bool
}

impl<'a> GrepParams<'a> {
    fn new(args: &Vec<String>) -> GrepParams {
        let query = &args[1];
        let file_path = &args[2];
        let ignore_case = env::var("IGNORE_CASE").is_ok();
        GrepParams {
            query,
            file_path,
            ignore_case,
        }
    }
}

pub fn grep(args: &Vec<String>) {
    // 校验入参合法性
    params_validate(args);

    // 根据入参构建Grep参数结构体
    let params: GrepParams<'_> = GrepParams::new(args);
    let read_string_result: Result<String, std::io::Error> = fs::read_to_string(params.file_path);
    
    // Grep结果处理
    match read_string_result {
        Ok(str) => find_query_content(&params.query, &str, params.ignore_case),
        Err(error) => match error.kind() {
            ErrorKind::NotFound => panic!("File not exist!"),
            ErrorKind::PermissionDenied => panic!("Permission denied!Please check user permission to this file!"),
            _ => panic!("Unknow error!")
        }
    }
}

fn params_validate(args: &Vec<String>) {
    if args.len() < 3 {
        panic!("Please input string and filepath.ex: minigrep test poem.txt");
    }
}

fn find_query_content(query: &str, content: &str, ignore_case: bool) {
    let result = if ignore_case {
        search_insensitive(query, content)
    } else {
        search(query, content)
    };
    for line in result{
        println!("{line}");
    }
}

fn search<'a>(query: &str, content: &'a str) -> Vec<&'a str> {
    content
        .lines()
        .filter(|line| line.contains(query))
        .collect()
}

fn search_insensitive<'a>(query: &str, content: &'a str) -> Vec<&'a str> {
    let query = query.to_lowercase();
    // let mut search_result = Vec::new();
    // for line in content.lines() {
    //     if line.to_lowercase().contains(&query) {
    //         search_result.push(line);
    //     }
    // }
    // search_result
    content
        .lines()
        .filter(|line| line.to_lowercase().contains(&query))
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic]
    fn param_not_enough() {
        let params = vec![String::from(""), String::from("to")];
        grep(&params);
    }

    #[test]
    fn test_search_case_sensitive() {
        let query = "duct";
        let contents = "\
Rust:
safe, fast, productive.
Pick three.
Duct tape.";
        assert_eq!(vec!["safe, fast, productive."], search(query, contents));
    }

    #[test]
    fn test_search_case_insensitive() {
        let query = "rUsT";
        let contents = "\
Rust:
safe, fast, productive.
Pick three.
Trust me.";
        assert_eq!(vec!["Rust:", "Trust me."], search_insensitive(query, contents));
    }
}
